from .. import statemachine

class ListWidgetStateMachine(statemachine.Machine):
  """State machine:
                          Move                      +------- Down --------+
                            ^                       |                     |
                            |                       |                     |
                            v         Big           V                     |
  [normal] -- down -->  [init_sel] -- Move --> [dragging] -- Up --> [maybe_drop]
     ^  ^                |     ^ ^              |      ^            ^    |
     |  |               Up     | |              |      |            |
     |  |                |     | +-------+      |      |           /  Debounce
     |  |                |     |         |      |      |          |      |
     |  |                |    Down       |      +-Move-+          V      |
     |  |                V     |         |                      Update   v
     |  +----Cancel---[maybe_tap]       +-------------Down---------[autoscroll]
     |                      |                                       ^    |
     |                  Debounce                                    |    |
     |                      |                                      / Speed = 0
     |                      V                                     |      |
     |                    [tap]                                   V      |
     |                      |                                  Update    |
     +-------------------------------------------------------------------+

  init_sel:
    OnEnter: Highlight item under cursor and save initial position.
    Move: Test if the move is substantial; if so, fire BigMove.

  tap:
    OnEnter: Run user callback, optionally unhighlight highlighted item. Go to
      normal.

  maybe_tap:
    OnEnter: Send Cancel on short tap, else start debounce
    Down: Cancel debounce

  dragging:
    OnEnter: Unhighlight highlighted item. Start scrolling timer.
    Move: Scroll list.

  maybe_drop
    OnEnter: Start debounce and update.
    OnExit: Cancel debounce.
    Update: Scroll list, apply friction.

  autoscroll:
    Update: Scroll list, apply friction.
    OnExit: Cancel update.
    Down: Save coords
    Speed=0: Stop timer."""
  EVENT_DOWN = "Down"
  EVENT_MOVE = "Move"
  EVENT_BIG_MOVE = "BigMove"
  EVENT_UP = "Up"
  EVENT_DEBOUNCE_TIMEOUT = "Debounce"
  EVENT_UPDATE_TIMEOUT = "Update"
  EVENT_SPEED_IS_0 = "Speed=0"
  EVENT_ACTION_DONE = "ActionDone"
  EVENT_CANCEL = "Cancel"

  def __init__(self, widget):
    normal = statemachine.State("normal")
    init_sel = statemachine.State("init_sel")
    maybe_tap = statemachine.State("maybe_tap")
    tap = statemachine.State("tap")
    dragging = statemachine.State("dragging")
    maybe_drop = statemachine.State("maybe_drop")
    autoscroll = statemachine.State("autoscroll")

    normal.SetEventCb(ListWidgetStateMachine.EVENT_DOWN, next=init_sel,
        cb=widget.SaveCoordsEvent)

    init_sel.SetEventCb(statemachine.ON_ENTER,
        cb=widget.HighlightCurrentAndSaveCoordsEvent)
    init_sel.SetEventCb(ListWidgetStateMachine.EVENT_UP, next=maybe_tap)
    init_sel.SetEventCb(ListWidgetStateMachine.EVENT_MOVE,
        cb=widget.TestForBigMoveEvent)
    init_sel.SetEventCb(ListWidgetStateMachine.EVENT_BIG_MOVE, next=dragging)

    maybe_tap.SetEventCb(ListWidgetStateMachine.EVENT_DOWN, next=init_sel,
        cb=widget.CancelDebounceEvent)
    maybe_tap.SetEventCb(ListWidgetStateMachine.EVENT_DEBOUNCE_TIMEOUT,
        next=tap)
    maybe_tap.SetEventCb(statemachine.ON_ENTER, cb=widget.CancelOrStartDebounceEvent)
    maybe_tap.SetEventCb(ListWidgetStateMachine.EVENT_CANCEL, next=normal)

    tap.SetEventCb(statemachine.ON_ENTER, cb=widget.TapEvent)
    tap.SetEventCb(ListWidgetStateMachine.EVENT_ACTION_DONE, next=normal)
    tap.SetEventCb(statemachine.ON_EXIT, cb=widget.UnhighlightEvent)

    dragging.SetEventCb(statemachine.ON_ENTER, cb=widget.UnhighlightEvent)
    dragging.SetEventCb(ListWidgetStateMachine.EVENT_MOVE, cb=widget.DragEvent)
    dragging.SetEventCb(ListWidgetStateMachine.EVENT_UP, next=maybe_drop,
        cb=widget.UpWhileDragEvent)

    maybe_drop.SetEventCb(statemachine.ON_ENTER,
        cb=widget.StartDebounceAndUpdateEvent)
    maybe_drop.SetEventCb(statemachine.ON_EXIT, cb=widget.CancelDebounceEvent,
        next=autoscroll)
    maybe_drop.SetEventCb(ListWidgetStateMachine.EVENT_DOWN, next=dragging,
        cb=widget.CancelUpdateEvent)
    # Hm, maybe unnecessary to call CancelDebounceEvent here because it's also
    # run OnExit from this state.
    maybe_drop.SetEventCb(ListWidgetStateMachine.EVENT_DEBOUNCE_TIMEOUT,
        cb=widget.CancelDebounceEvent, next=autoscroll)
    maybe_drop.SetEventCb(ListWidgetStateMachine.EVENT_UPDATE_TIMEOUT,
        cb=widget.UpdateEvent)

    autoscroll.SetEventCb(ListWidgetStateMachine.EVENT_DOWN, next=init_sel,
        cb=widget.SaveCoordsEvent)
    autoscroll.SetEventCb(ListWidgetStateMachine.EVENT_UPDATE_TIMEOUT,
        cb=widget.UpdateEvent)
    autoscroll.SetEventCb(ListWidgetStateMachine.EVENT_SPEED_IS_0,
        cb=widget.CancelUpdateEvent, next=normal)

    statemachine.Machine.__init__(self, normal)

